1   /*
2    * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.font;
27  
28  import java.lang.ref.SoftReference;
29  import java.lang.ref.WeakReference;
30  import java.awt.Font;
31  import java.awt.GraphicsEnvironment;
32  import java.awt.Rectangle;
33  import java.awt.geom.AffineTransform;
34  import java.awt.geom.GeneralPath;
35  import java.awt.geom.NoninvertibleTransformException;
36  import java.awt.geom.Point2D;
37  import java.awt.geom.Rectangle2D;
38  import java.util.concurrent.ConcurrentHashMap;
39  import static sun.awt.SunHints.*;
40  
41  
42  public class FileFontStrike extends PhysicalStrike {
43  
44      /* fffe and ffff are values we specially interpret as meaning
45       * invisible glyphs.
46       */
47      static final int INVISIBLE_GLYPHS = 0x0fffe;
48  
49      private FileFont fileFont;
50  
51      /* REMIND: replace this scheme with one that installs a cache
52       * instance of the appropriate type. It will require changes in
53       * FontStrikeDisposer and NativeStrike etc.
54       */
55      private static final int UNINITIALISED = 0;
56      private static final int INTARRAY      = 1;
57      private static final int LONGARRAY     = 2;
58      private static final int SEGINTARRAY   = 3;
59      private static final int SEGLONGARRAY  = 4;
60  
61      private volatile int glyphCacheFormat = UNINITIALISED;
62  
63      /* segmented arrays are blocks of 32 */
64      private static final int SEGSHIFT = 5;
65      private static final int SEGSIZE  = 1 << SEGSHIFT;
66  
67      private boolean segmentedCache;
68      private int[][] segIntGlyphImages;
69      private long[][] segLongGlyphImages;
70  
71      /* The "metrics" information requested by clients is usually nothing
72       * more than the horizontal advance of the character.
73       * In most cases this advance and other metrics information is stored
74       * in the glyph image cache.
75       * But in some cases we do not automatically retrieve the glyph
76       * image when the advance is requested. In those cases we want to
77       * cache the advances since this has been shown to be important for
78       * performance.
79       * The segmented cache is used in cases when the single array
80       * would be too large.
81       */
82      private float[] horizontalAdvances;
83      private float[][] segHorizontalAdvances;
84  
85      /* Outline bounds are used when printing and when drawing outlines
86       * to the screen. On balance the relative rarity of these cases
87       * and the fact that getting this requires generating a path at
88       * the scaler level means that its probably OK to store these
89       * in a Java-level hashmap as the trade-off between time and space.
90       * Later can revisit whether to cache these at all, or elsewhere.
91       * Should also profile whether subsequent to getting the bounds, the
92       * outline itself is also requested. The 1.4 implementation doesn't
93       * cache outlines so you could generate the path twice - once to get
94       * the bounds and again to return the outline to the client.
95       * If the two uses are coincident then also look into caching outlines.
96       * One simple optimisation is that we could store the last single
97       * outline retrieved. This assumes that bounds then outline will always
98       * be retrieved for a glyph rather than retrieving bounds for all glyphs
99       * then outlines for all glyphs.
100      */
101     ConcurrentHashMap<Integer, Rectangle2D.Float> boundsMap;
102     SoftReference<ConcurrentHashMap<Integer, Point2D.Float>>
103         glyphMetricsMapRef;
104 
105     AffineTransform invertDevTx;
106 
107     boolean useNatives;
108     NativeStrike[] nativeStrikes;
109 
110     /* Used only for communication to native layer */
111     private int intPtSize;
112 
113     /* Perform global initialisation needed for Windows native rasterizer */
114     private static native boolean initNative();
115     private static boolean isXPorLater = false;
116     static {
117         if (FontUtilities.isWindows && !FontUtilities.useT2K &&
118             !GraphicsEnvironment.isHeadless()) {
119             isXPorLater = initNative();
120         }
121     }
122 
123     FileFontStrike(FileFont fileFont, FontStrikeDesc desc) {
124         super(fileFont, desc);
125         this.fileFont = fileFont;
126 
127         if (desc.style != fileFont.style) {
128           /* If using algorithmic styling, the base values are
129            * boldness = 1.0, italic = 0.0. The superclass constructor
130            * initialises these.
131            */
132             if ((desc.style & Font.ITALIC) == Font.ITALIC &&
133                 (fileFont.style & Font.ITALIC) == 0) {
134                 algoStyle = true;
135                 italic = 0.7f;
136             }
137             if ((desc.style & Font.BOLD) == Font.BOLD &&
138                 ((fileFont.style & Font.BOLD) == 0)) {
139                 algoStyle = true;
140                 boldness = 1.33f;
141             }
142         }
143         double[] matrix = new double[4];
144         AffineTransform at = desc.glyphTx;
145         at.getMatrix(matrix);
146         if (!desc.devTx.isIdentity() &&
147             desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
148             try {
149                 invertDevTx = desc.devTx.createInverse();
150             } catch (NoninvertibleTransformException e) {
151             }
152         }
153 
154         /* Amble fonts are better rendered unhinted although there's the
155          * inevitable fuzziness that accompanies this due to no longer
156          * snapping stems to the pixel grid. The exception is that in B&W
157          * mode they are worse without hinting. The down side to that is that
158          * B&W metrics will differ which normally isn't the case, although
159          * since AA mode is part of the measuring context that should be OK.
160          * We don't expect Amble to be installed in the Windows fonts folder.
161          * If we were to, then we'd also might want to disable using the
162          * native rasteriser path which is used for LCD mode for platform
163          * fonts. since we have no way to disable hinting by GDI.
164          * In the case of Amble, since its 'gasp' table says to disable
165          * hinting, I'd expect GDI to follow that, so likely it should
166          * all be consistent even if GDI used.
167          */
168         boolean disableHinting = desc.aaHint != INTVAL_TEXT_ANTIALIAS_OFF &&
169                                  fileFont.familyName.startsWith("Amble");
170 
171         /* If any of the values is NaN then substitute the null scaler context.
172          * This will return null images, zero advance, and empty outlines
173          * as no rendering need take place in this case.
174          * We pass in the null scaler as the singleton null context
175          * requires it. However
176          */
177         if (Double.isNaN(matrix[0]) || Double.isNaN(matrix[1]) ||
178             Double.isNaN(matrix[2]) || Double.isNaN(matrix[3]) ||
179             fileFont.getScaler() == null) {
180             pScalerContext = NullFontScaler.getNullScalerContext();
181         } else {
182             pScalerContext = fileFont.getScaler().createScalerContext(matrix,
183                                     desc.aaHint, desc.fmHint,
184                                     boldness, italic, disableHinting);
185         }
186 
187         mapper = fileFont.getMapper();
188         int numGlyphs = mapper.getNumGlyphs();
189 
190         /* Always segment for fonts with > 256 glyphs, but also for smaller
191          * fonts with non-typical sizes and transforms.
192          * Segmenting for all non-typical pt sizes helps to minimise memory
193          * usage when very many distinct strikes are created.
194          * The size range of 0->5 and 37->INF for segmenting is arbitrary
195          * but the intention is that typical GUI integer point sizes (6->36)
196          * should not segment unless there's another reason to do so.
197          */
198         float ptSize = (float)matrix[3]; // interpreted only when meaningful.
199         int iSize = intPtSize = (int)ptSize;
200         boolean isSimpleTx = (at.getType() & complexTX) == 0;
201         segmentedCache =
202             (numGlyphs > SEGSIZE << 3) ||
203             ((numGlyphs > SEGSIZE << 1) &&
204              (!isSimpleTx || ptSize != iSize || iSize < 6 || iSize > 36));
205 
206         /* This can only happen if we failed to allocate memory for context.
207          * NB: in such case we may still have some memory in java heap
208          *     but subsequent attempt to allocate null scaler context
209          *     may fail too (cause it is allocate in the native heap).
210          *     It is not clear how to make this more robust but on the
211          *     other hand getting NULL here seems to be extremely unlikely.
212          */
213         if (pScalerContext == 0L) {
214             /* REMIND: when the code is updated to install cache objects
215              * rather than using a switch this will be more efficient.
216              */
217             this.disposer = new FontStrikeDisposer(fileFont, desc);
218             initGlyphCache();
219             pScalerContext = NullFontScaler.getNullScalerContext();
220             SunFontManager.getInstance().deRegisterBadFont(fileFont);
221             return;
222         }
223         /* First, see if native code should be used to create the glyph.
224          * GDI will return the integer metrics, not fractional metrics, which
225          * may be requested for this strike, so we would require here that :
226          * desc.fmHint != INTVAL_FRACTIONALMETRICS_ON
227          * except that the advance returned by GDI is always overwritten by
228          * the JDK rasteriser supplied one (see getGlyphImageFromWindows()).
229          */
230         if (FontUtilities.isWindows && isXPorLater &&
231             !FontUtilities.useT2K &&
232             !GraphicsEnvironment.isHeadless() &&
233             !fileFont.useJavaRasterizer &&
234             (desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
235              desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) &&
236             (matrix[1] == 0.0 && matrix[2] == 0.0 &&
237              matrix[0] == matrix[3] &&
238              matrix[0] >= 3.0 && matrix[0] <= 100.0) &&
239             !((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) {
240             useNatives = true;
241         }
242         else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) {
243             /* Check its a simple scale of a pt size in the range
244              * where native bitmaps typically exist (6-36 pts) */
245             if (matrix[1] == 0.0 && matrix[2] == 0.0 &&
246                 matrix[0] >= 6.0 && matrix[0] <= 36.0 &&
247                 matrix[0] == matrix[3]) {
248                 useNatives = true;
249                 int numNatives = fileFont.nativeFonts.length;
250                 nativeStrikes = new NativeStrike[numNatives];
251                 /* Maybe initialise these strikes lazily?. But we
252                  * know we need at least one
253                  */
254                 for (int i=0; i<numNatives; i++) {
255                     nativeStrikes[i] =
256                         new NativeStrike(fileFont.nativeFonts[i], desc, false);
257                 }
258             }
259         }
260         if (FontUtilities.isLogging() && FontUtilities.isWindows) {
261             FontUtilities.getLogger().info
262                 ("Strike for " + fileFont + " at size = " + intPtSize +
263                  " use natives = " + useNatives +
264                  " useJavaRasteriser = " + fileFont.useJavaRasterizer +
265                  " AAHint = " + desc.aaHint +
266                  " Has Embedded bitmaps = " +
267                  ((TrueTypeFont)fileFont).
268                  useEmbeddedBitmapsForSize(intPtSize));
269         }
270         this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext);
271 
272         /* Always get the image and the advance together for smaller sizes
273          * that are likely to be important to rendering performance.
274          * The pixel size of 48.0 can be thought of as
275          * "maximumSizeForGetImageWithAdvance".
276          * This should be no greater than OutlineTextRender.THRESHOLD.
277          */
278         double maxSz = 48.0;
279         getImageWithAdvance =
280             Math.abs(at.getScaleX()) <= maxSz &&
281             Math.abs(at.getScaleY()) <= maxSz &&
282             Math.abs(at.getShearX()) <= maxSz &&
283             Math.abs(at.getShearY()) <= maxSz;
284 
285         /* Some applications request advance frequently during layout.
286          * If we are not getting and caching the image with the advance,
287          * there is a potentially significant performance penalty if the
288          * advance is repeatedly requested before requesting the image.
289          * We should at least cache the horizontal advance.
290          * REMIND: could use info in the font, eg hmtx, to retrieve some
291          * advances. But still want to cache it here.
292          */
293 
294         if (!getImageWithAdvance) {
295             if (!segmentedCache) {
296                 horizontalAdvances = new float[numGlyphs];
297                 /* use max float as uninitialised advance */
298                 for (int i=0; i<numGlyphs; i++) {
299                     horizontalAdvances[i] = Float.MAX_VALUE;
300                 }
301             } else {
302                 int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
303                 segHorizontalAdvances = new float[numSegments][];
304             }
305         }
306     }
307 
308     /* A number of methods are delegated by the strike to the scaler
309      * context which is a shared resource on a physical font.
310      */
311 
312     public int getNumGlyphs() {
313         return fileFont.getNumGlyphs();
314     }
315 
316     long getGlyphImageFromNative(int glyphCode) {
317         if (FontUtilities.isWindows) {
318             return getGlyphImageFromWindows(glyphCode);
319         } else {
320             return getGlyphImageFromX11(glyphCode);
321         }
322     }
323 
324     /* There's no global state conflicts, so this method is not
325      * presently synchronized.
326      */
327     private native long _getGlyphImageFromWindows(String family,
328                                                   int style,
329                                                   int size,
330                                                   int glyphCode,
331                                                   boolean fracMetrics);
332 
333     long getGlyphImageFromWindows(int glyphCode) {
334         String family = fileFont.getFamilyName(null);
335         int style = desc.style & Font.BOLD | desc.style & Font.ITALIC
336             | fileFont.getStyle();
337         int size = intPtSize;
338         long ptr = _getGlyphImageFromWindows
339             (family, style, size, glyphCode,
340              desc.fmHint == INTVAL_FRACTIONALMETRICS_ON);
341         if (ptr != 0) {
342             /* Get the advance from the JDK rasterizer. This is mostly
343              * necessary for the fractional metrics case, but there are
344              * also some very small number (<0.25%) of marginal cases where
345              * there is some rounding difference between windows and JDK.
346              * After these are resolved, we can restrict this extra
347              * work to the FM case.
348              */
349             float advance = getGlyphAdvance(glyphCode, false);
350             StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset,
351                                         advance);
352             return ptr;
353         } else {
354             return fileFont.getGlyphImage(pScalerContext, glyphCode);
355         }
356     }
357 
358     /* Try the native strikes first, then try the fileFont strike */
359     long getGlyphImageFromX11(int glyphCode) {
360         long glyphPtr;
361         char charCode = fileFont.glyphToCharMap[glyphCode];
362         for (int i=0;i<nativeStrikes.length;i++) {
363             CharToGlyphMapper mapper = fileFont.nativeFonts[i].getMapper();
364             int gc = mapper.charToGlyph(charCode)&0xffff;
365             if (gc != mapper.getMissingGlyphCode()) {
366                 glyphPtr = nativeStrikes[i].getGlyphImagePtrNoCache(gc);
367                 if (glyphPtr != 0L) {
368                     return glyphPtr;
369                 }
370             }
371         }
372         return fileFont.getGlyphImage(pScalerContext, glyphCode);
373     }
374 
375     long getGlyphImagePtr(int glyphCode) {
376         if (glyphCode >= INVISIBLE_GLYPHS) {
377             return StrikeCache.invisibleGlyphPtr;
378         }
379         long glyphPtr = 0L;
380         if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) {
381             return glyphPtr;
382         } else {
383             if (useNatives) {
384                 glyphPtr = getGlyphImageFromNative(glyphCode);
385                 if (glyphPtr == 0L && FontUtilities.isLogging()) {
386                     FontUtilities.getLogger().info
387                         ("Strike for " + fileFont +
388                          " at size = " + intPtSize +
389                          " couldn't get native glyph for code = " + glyphCode);
390                  }
391             } if (glyphPtr == 0L) {
392                 glyphPtr = fileFont.getGlyphImage(pScalerContext,
393                                                   glyphCode);
394             }
395             return setCachedGlyphPtr(glyphCode, glyphPtr);
396         }
397     }
398 
399     void getGlyphImagePtrs(int[] glyphCodes, long[] images, int  len) {
400 
401         for (int i=0; i<len; i++) {
402             int glyphCode = glyphCodes[i];
403             if (glyphCode >= INVISIBLE_GLYPHS) {
404                 images[i] = StrikeCache.invisibleGlyphPtr;
405                 continue;
406             } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
407                 continue;
408             } else {
409                 long glyphPtr = 0L;
410                 if (useNatives) {
411                     glyphPtr = getGlyphImageFromNative(glyphCode);
412                 } if (glyphPtr == 0L) {
413                     glyphPtr = fileFont.getGlyphImage(pScalerContext,
414                                                       glyphCode);
415                 }
416                 images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
417             }
418         }
419     }
420 
421     /* The following method is called from CompositeStrike as a special case.
422      */
423     private static final int SLOTZEROMAX = 0xffffff;
424     int getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len) {
425 
426         int convertedCnt = 0;
427 
428         for (int i=0; i<len; i++) {
429             int glyphCode = glyphCodes[i];
430             if (glyphCode >= SLOTZEROMAX) {
431                 return convertedCnt;
432             } else {
433                 convertedCnt++;
434             }
435             if (glyphCode >= INVISIBLE_GLYPHS) {
436                 images[i] = StrikeCache.invisibleGlyphPtr;
437                 continue;
438             } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
439                 continue;
440             } else {
441                 long glyphPtr = 0L;
442                 if (useNatives) {
443                     glyphPtr = getGlyphImageFromNative(glyphCode);
444                 }
445                 if (glyphPtr == 0L) {
446                     glyphPtr = fileFont.getGlyphImage(pScalerContext,
447                                                       glyphCode);
448                 }
449                 images[i] = setCachedGlyphPtr(glyphCode, glyphPtr);
450             }
451         }
452         return convertedCnt;
453     }
454 
455     /* Only look in the cache */
456     long getCachedGlyphPtr(int glyphCode) {
457         switch (glyphCacheFormat) {
458             case INTARRAY:
459                 return intGlyphImages[glyphCode] & INTMASK;
460             case SEGINTARRAY:
461                 int segIndex = glyphCode >> SEGSHIFT;
462                 if (segIntGlyphImages[segIndex] != null) {
463                     int subIndex = glyphCode % SEGSIZE;
464                     return segIntGlyphImages[segIndex][subIndex] & INTMASK;
465                 } else {
466                     return 0L;
467                 }
468             case LONGARRAY:
469                 return longGlyphImages[glyphCode];
470             case SEGLONGARRAY:
471                 segIndex = glyphCode >> SEGSHIFT;
472                 if (segLongGlyphImages[segIndex] != null) {
473                     int subIndex = glyphCode % SEGSIZE;
474                     return segLongGlyphImages[segIndex][subIndex];
475                 } else {
476                     return 0L;
477                 }
478         }
479         /* If reach here cache is UNINITIALISED. */
480         return 0L;
481     }
482 
483     private synchronized long setCachedGlyphPtr(int glyphCode, long glyphPtr) {
484         switch (glyphCacheFormat) {
485             case INTARRAY:
486                 if (intGlyphImages[glyphCode] == 0) {
487                     intGlyphImages[glyphCode] = (int)glyphPtr;
488                     return glyphPtr;
489                 } else {
490                     StrikeCache.freeIntPointer((int)glyphPtr);
491                     return intGlyphImages[glyphCode] & INTMASK;
492                 }
493 
494             case SEGINTARRAY:
495                 int segIndex = glyphCode >> SEGSHIFT;
496                 int subIndex = glyphCode % SEGSIZE;
497                 if (segIntGlyphImages[segIndex] == null) {
498                     segIntGlyphImages[segIndex] = new int[SEGSIZE];
499                 }
500                 if (segIntGlyphImages[segIndex][subIndex] == 0) {
501                     segIntGlyphImages[segIndex][subIndex] = (int)glyphPtr;
502                     return glyphPtr;
503                 } else {
504                     StrikeCache.freeIntPointer((int)glyphPtr);
505                     return segIntGlyphImages[segIndex][subIndex] & INTMASK;
506                 }
507 
508             case LONGARRAY:
509                 if (longGlyphImages[glyphCode] == 0L) {
510                     longGlyphImages[glyphCode] = glyphPtr;
511                     return glyphPtr;
512                 } else {
513                     StrikeCache.freeLongPointer(glyphPtr);
514                     return longGlyphImages[glyphCode];
515                 }
516 
517            case SEGLONGARRAY:
518                 segIndex = glyphCode >> SEGSHIFT;
519                 subIndex = glyphCode % SEGSIZE;
520                 if (segLongGlyphImages[segIndex] == null) {
521                     segLongGlyphImages[segIndex] = new long[SEGSIZE];
522                 }
523                 if (segLongGlyphImages[segIndex][subIndex] == 0L) {
524                     segLongGlyphImages[segIndex][subIndex] = glyphPtr;
525                     return glyphPtr;
526                 } else {
527                     StrikeCache.freeLongPointer(glyphPtr);
528                     return segLongGlyphImages[segIndex][subIndex];
529                 }
530         }
531 
532         /* Reach here only when the cache is not initialised which is only
533          * for the first glyph to be initialised in the strike.
534          * Initialise it and recurse. Note that we are already synchronized.
535          */
536         initGlyphCache();
537         return setCachedGlyphPtr(glyphCode, glyphPtr);
538     }
539 
540     /* Called only from synchronized code or constructor */
541     private synchronized void initGlyphCache() {
542 
543         int numGlyphs = mapper.getNumGlyphs();
544         int tmpFormat = UNINITIALISED;
545         if (segmentedCache) {
546             int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE;
547             if (longAddresses) {
548                 tmpFormat = SEGLONGARRAY;
549                 segLongGlyphImages = new long[numSegments][];
550                 this.disposer.segLongGlyphImages = segLongGlyphImages;
551              } else {
552                  tmpFormat = SEGINTARRAY;
553                  segIntGlyphImages = new int[numSegments][];
554                  this.disposer.segIntGlyphImages = segIntGlyphImages;
555              }
556         } else {
557             if (longAddresses) {
558                 tmpFormat = LONGARRAY;
559                 longGlyphImages = new long[numGlyphs];
560                 this.disposer.longGlyphImages = longGlyphImages;
561             } else {
562                 tmpFormat = INTARRAY;
563                 intGlyphImages = new int[numGlyphs];
564                 this.disposer.intGlyphImages = intGlyphImages;
565             }
566         }
567         glyphCacheFormat = tmpFormat;
568     }
569 
570     float getGlyphAdvance(int glyphCode) {
571         return getGlyphAdvance(glyphCode, true);
572     }
573 
574     /* Metrics info is always retrieved. If the GlyphInfo address is non-zero
575      * then metrics info there is valid and can just be copied.
576      * This is in user space coordinates unless getUserAdv == false.
577      * Device space advance should not be propagated out of this class.
578      */
579     private float getGlyphAdvance(int glyphCode, boolean getUserAdv) {
580         float advance;
581 
582         if (glyphCode >= INVISIBLE_GLYPHS) {
583             return 0f;
584         }
585 
586         /* Notes on the (getUserAdv == false) case.
587          *
588          * Setting getUserAdv == false is internal to this class.
589          * If there's no graphics transform we can let
590          * getGlyphAdvance take its course, and potentially caching in
591          * advances arrays, except for signalling that
592          * getUserAdv == false means there is no need to create an image.
593          * It is possible that code already calculated the user advance,
594          * and it is desirable to take advantage of that work.
595          * But, if there's a transform and we want device advance, we
596          * can't use any values cached in the advances arrays - unless
597          * first re-transform them into device space using 'desc.devTx'.
598          * invertDevTx is null if the graphics transform is identity,
599          * a translate, or non-invertible. The latter case should
600          * not ever occur in the getUserAdv == false path.
601          * In other words its either null, or the inversion of a
602          * simple uniform scale. If its null, we can populate and
603          * use the advance caches as normal.
604          *
605          * If we don't find a cached value, obtain the device advance and
606          * return it. This will get stashed on the image by the caller and any
607          * subsequent metrics calls will be able to use it as is the case
608          * whenever an image is what is initially requested.
609          *
610          * Don't query if there's a value cached on the image, since this
611          * getUserAdv==false code path is entered solely when none exists.
612          */
613         if (horizontalAdvances != null) {
614             advance = horizontalAdvances[glyphCode];
615             if (advance != Float.MAX_VALUE) {
616                 if (!getUserAdv && invertDevTx != null) {
617                     Point2D.Float metrics = new Point2D.Float(advance, 0f);
618                     desc.devTx.deltaTransform(metrics, metrics);
619                     return metrics.x;
620                 } else {
621                     return advance;
622                 }
623             }
624         } else if (segmentedCache && segHorizontalAdvances != null) {
625             int segIndex = glyphCode >> SEGSHIFT;
626             float[] subArray = segHorizontalAdvances[segIndex];
627             if (subArray != null) {
628                 advance = subArray[glyphCode % SEGSIZE];
629                 if (advance != Float.MAX_VALUE) {
630                     if (!getUserAdv && invertDevTx != null) {
631                         Point2D.Float metrics = new Point2D.Float(advance, 0f);
632                         desc.devTx.deltaTransform(metrics, metrics);
633                         return metrics.x;
634                     } else {
635                         return advance;
636                     }
637                 }
638             }
639         }
640 
641         if (!getUserAdv && invertDevTx != null) {
642             Point2D.Float metrics = new Point2D.Float();
643             fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
644             return metrics.x;
645         }
646 
647         if (invertDevTx != null || !getUserAdv) {
648             /* If there is a device transform need x & y advance to
649              * transform back into user space.
650              */
651             advance = getGlyphMetrics(glyphCode, getUserAdv).x;
652         } else {
653             long glyphPtr;
654             if (getImageWithAdvance) {
655                 /* A heuristic optimisation says that for most cases its
656                  * worthwhile retrieving the image at the same time as the
657                  * advance. So here we get the image data even if its not
658                  * already cached.
659                  */
660                 glyphPtr = getGlyphImagePtr(glyphCode);
661             } else {
662                 glyphPtr = getCachedGlyphPtr(glyphCode);
663             }
664             if (glyphPtr != 0L) {
665                 advance = StrikeCache.unsafe.getFloat
666                     (glyphPtr + StrikeCache.xAdvanceOffset);
667 
668             } else {
669                 advance = fileFont.getGlyphAdvance(pScalerContext, glyphCode);
670             }
671         }
672 
673         if (horizontalAdvances != null) {
674             horizontalAdvances[glyphCode] = advance;
675         } else if (segmentedCache && segHorizontalAdvances != null) {
676             int segIndex = glyphCode >> SEGSHIFT;
677             int subIndex = glyphCode % SEGSIZE;
678             if (segHorizontalAdvances[segIndex] == null) {
679                 segHorizontalAdvances[segIndex] = new float[SEGSIZE];
680                 for (int i=0; i<SEGSIZE; i++) {
681                      segHorizontalAdvances[segIndex][i] = Float.MAX_VALUE;
682                 }
683             }
684             segHorizontalAdvances[segIndex][subIndex] = advance;
685         }
686         return advance;
687     }
688 
689     float getCodePointAdvance(int cp) {
690         return getGlyphAdvance(mapper.charToGlyph(cp));
691     }
692 
693     /**
694      * Result and pt are both in device space.
695      */
696     void getGlyphImageBounds(int glyphCode, Point2D.Float pt,
697                              Rectangle result) {
698 
699         long ptr = getGlyphImagePtr(glyphCode);
700         float topLeftX, topLeftY;
701 
702         /* With our current design NULL ptr is not possible
703            but if we eventually allow scalers to return NULL pointers
704            this check might be actually useful. */
705         if (ptr == 0L) {
706             result.x = (int) Math.floor(pt.x);
707             result.y = (int) Math.floor(pt.y);
708             result.width = result.height = 0;
709             return;
710         }
711 
712         topLeftX = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftXOffset);
713         topLeftY = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftYOffset);
714 
715         result.x = (int)Math.floor(pt.x + topLeftX);
716         result.y = (int)Math.floor(pt.y + topLeftY);
717         result.width =
718             StrikeCache.unsafe.getShort(ptr+StrikeCache.widthOffset)  &0x0ffff;
719         result.height =
720             StrikeCache.unsafe.getShort(ptr+StrikeCache.heightOffset) &0x0ffff;
721 
722         /* HRGB LCD text may have padding that is empty. This is almost always
723          * going to be when topLeftX is -2 or less.
724          * Try to return a tighter bounding box in that case.
725          * If the first three bytes of every row are all zero, then
726          * add 1 to "x" and reduce "width" by 1.
727          */
728         if ((desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
729              desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR)
730             && topLeftX <= -2.0f) {
731             int minx = getGlyphImageMinX(ptr, (int)result.x);
732             if (minx > result.x) {
733                 result.x += 1;
734                 result.width -=1;
735             }
736         }
737     }
738 
739     private int getGlyphImageMinX(long ptr, int origMinX) {
740 
741         int width = StrikeCache.unsafe.getChar(ptr+StrikeCache.widthOffset);
742         int height = StrikeCache.unsafe.getChar(ptr+StrikeCache.heightOffset);
743         int rowBytes =
744             StrikeCache.unsafe.getChar(ptr+StrikeCache.rowBytesOffset);
745 
746         if (rowBytes == width) {
747             return origMinX;
748         }
749 
750         long pixelData;
751         if (StrikeCache.nativeAddressSize == 4) {
752             pixelData = 0xffffffff &
753                 StrikeCache.unsafe.getInt(ptr + StrikeCache.pixelDataOffset);
754         } else {
755             pixelData =
756                 StrikeCache.unsafe.getLong(ptr + StrikeCache.pixelDataOffset);
757         }
758         if (pixelData == 0L) {
759             return origMinX;
760         }
761 
762         for (int y=0;y<height;y++) {
763             for (int x=0;x<3;x++) {
764                 if (StrikeCache.unsafe.getByte(pixelData+y*rowBytes+x) != 0) {
765                     return origMinX;
766                 }
767             }
768         }
769         return origMinX+1;
770     }
771 
772     /* These 3 metrics methods below should be implemented to return
773      * values in user space.
774      */
775     StrikeMetrics getFontMetrics() {
776         if (strikeMetrics == null) {
777             strikeMetrics =
778                 fileFont.getFontMetrics(pScalerContext);
779             if (invertDevTx != null) {
780                 strikeMetrics.convertToUserSpace(invertDevTx);
781             }
782         }
783         return strikeMetrics;
784     }
785 
786     Point2D.Float getGlyphMetrics(int glyphCode) {
787         return getGlyphMetrics(glyphCode, true);
788     }
789 
790     private Point2D.Float getGlyphMetrics(int glyphCode, boolean getImage) {
791         Point2D.Float metrics = new Point2D.Float();
792 
793         // !!! or do we force sgv user glyphs?
794         if (glyphCode >= INVISIBLE_GLYPHS) {
795             return metrics;
796         }
797         long glyphPtr;
798         if (getImageWithAdvance && getImage) {
799             /* A heuristic optimisation says that for most cases its
800              * worthwhile retrieving the image at the same time as the
801              * metrics. So here we get the image data even if its not
802              * already cached.
803              */
804             glyphPtr = getGlyphImagePtr(glyphCode);
805         } else {
806              glyphPtr = getCachedGlyphPtr(glyphCode);
807         }
808         if (glyphPtr != 0L) {
809             metrics = new Point2D.Float();
810             metrics.x = StrikeCache.unsafe.getFloat
811                 (glyphPtr + StrikeCache.xAdvanceOffset);
812             metrics.y = StrikeCache.unsafe.getFloat
813                 (glyphPtr + StrikeCache.yAdvanceOffset);
814             /* advance is currently in device space, need to convert back
815              * into user space.
816              * This must not include the translation component. */
817             if (invertDevTx != null) {
818                 invertDevTx.deltaTransform(metrics, metrics);
819             }
820         } else {
821             /* We sometimes cache these metrics as they are expensive to
822              * generate for large glyphs.
823              * We never reach this path if we obtain images with advances.
824              * But if we do not obtain images with advances its possible that
825              * we first obtain this information, then the image, and never
826              * will access this value again.
827              */
828             Integer key = Integer.valueOf(glyphCode);
829             Point2D.Float value = null;
830             ConcurrentHashMap<Integer, Point2D.Float> glyphMetricsMap = null;
831             if (glyphMetricsMapRef != null) {
832                 glyphMetricsMap = glyphMetricsMapRef.get();
833             }
834             if (glyphMetricsMap != null) {
835                 value = glyphMetricsMap.get(key);
836                 if (value != null) {
837                     metrics.x = value.x;
838                     metrics.y = value.y;
839                     /* already in user space */
840                     return metrics;
841                 }
842             }
843             if (value == null) {
844                 fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
845                 /* advance is currently in device space, need to convert back
846                  * into user space.
847                  */
848                 if (invertDevTx != null) {
849                     invertDevTx.deltaTransform(metrics, metrics);
850                 }
851                 value = new Point2D.Float(metrics.x, metrics.y);
852                 /* We aren't synchronizing here so it is possible to
853                  * overwrite the map with another one but this is harmless.
854                  */
855                 if (glyphMetricsMap == null) {
856                     glyphMetricsMap =
857                         new ConcurrentHashMap<Integer, Point2D.Float>();
858                     glyphMetricsMapRef =
859                         new SoftReference<ConcurrentHashMap<Integer,
860                         Point2D.Float>>(glyphMetricsMap);
861                 }
862                 glyphMetricsMap.put(key, value);
863             }
864         }
865         return metrics;
866     }
867 
868     Point2D.Float getCharMetrics(char ch) {
869         return getGlyphMetrics(mapper.charToGlyph(ch));
870     }
871 
872     /* The caller of this can be trusted to return a copy of this
873      * return value rectangle to public API. In fact frequently it
874      * can't use use this return value directly anyway.
875      * This returns bounds in device space. Currently the only
876      * caller is SGV and it converts back to user space.
877      * We could change things so that this code does the conversion so
878      * that all coords coming out of the font system are converted back
879      * into user space even if they were measured in device space.
880      * The same applies to the other methods that return outlines (below)
881      * But it may make particular sense for this method that caches its
882      * results.
883      * There'd be plenty of exceptions, to this too, eg getGlyphPoint needs
884      * device coords as its called from native layout and getGlyphImageBounds
885      * is used by GlyphVector.getGlyphPixelBounds which is specified to
886      * return device coordinates, the image pointers aren't really used
887      * up in Java code either.
888      */
889     Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
890 
891         if (boundsMap == null) {
892             boundsMap = new ConcurrentHashMap<Integer, Rectangle2D.Float>();
893         }
894 
895         Integer key = Integer.valueOf(glyphCode);
896         Rectangle2D.Float bounds = boundsMap.get(key);
897 
898         if (bounds == null) {
899             bounds = fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
900             boundsMap.put(key, bounds);
901         }
902         return bounds;
903     }
904 
905     public Rectangle2D getOutlineBounds(int glyphCode) {
906         return fileFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
907     }
908 
909     private
910         WeakReference<ConcurrentHashMap<Integer,GeneralPath>> outlineMapRef;
911 
912     GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
913 
914         GeneralPath gp = null;
915         ConcurrentHashMap<Integer, GeneralPath> outlineMap = null;
916 
917         if (outlineMapRef != null) {
918             outlineMap = outlineMapRef.get();
919             if (outlineMap != null) {
920                 gp = (GeneralPath)outlineMap.get(glyphCode);
921             }
922         }
923 
924         if (gp == null) {
925             gp = fileFont.getGlyphOutline(pScalerContext, glyphCode, 0, 0);
926             if (outlineMap == null) {
927                 outlineMap = new ConcurrentHashMap<Integer, GeneralPath>();
928                 outlineMapRef =
929                    new WeakReference
930                        <ConcurrentHashMap<Integer,GeneralPath>>(outlineMap);
931             }
932             outlineMap.put(glyphCode, gp);
933         }
934         gp = (GeneralPath)gp.clone(); // mutable!
935         if (x != 0f || y != 0f) {
936             gp.transform(AffineTransform.getTranslateInstance(x, y));
937         }
938         return gp;
939     }
940 
941     GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
942         return fileFont.getGlyphVectorOutline(pScalerContext,
943                                               glyphs, glyphs.length, x, y);
944     }
945 
946     protected void adjustPoint(Point2D.Float pt) {
947         if (invertDevTx != null) {
948             invertDevTx.deltaTransform(pt, pt);
949         }
950     }
951 }